package com.idata.service.services;

import com.alibaba.fastjson2.JSONObject;
import com.idata.common.constant.KGUrl;
import com.idata.common.constant.RedisKeyConstant;
import com.idata.model.dto.DisputeEntityBuilderDto;
import com.idata.model.entity.DisputeEntity;
import com.idata.model.enums.AssociationTypeEnum;
import com.idata.model.enums.DisputeSourceEnum;
import com.idata.model.enums.StatisticsTypeForInputEnum;
import com.idata.model.interfaces.HasDisputeTimeSortField;
import com.idata.model.interfaces.HasDisputeType;
import com.idata.model.proxyDO.CollaborativeCaseDO;
import com.idata.model.proxyDO.KGBaseDisputeInfoDO;
import com.idata.model.proxyDO.Police110CaseDO;
import com.idata.service.builder.DisputeEntityBuilder;
import com.idata.service.config.ProxyUrlProperties;
import com.idata.service.utils.SortDisputeInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @File DisputeService.Java
 * @Modify Time      @Author    @Version    @Description
 * ------------      -------    --------    -----------
 * 2023/10/15         yd.j     1.0          纠纷服务相关
 */
@Slf4j
@Service
public class DisputeService {

    @Autowired
    private RestTemplate restTemplate;
    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private ProxyUrlProperties proxyUrlProperties;

    /**
     * 将图谱分析的人事纠纷放入到 Redis 中
     * @param idCard 身份证号
     * @param value  人事纠纷事件
     */
    private void putDisputeEntityToRedis(String idCard, String value) {
        redisTemplate.opsForValue().set(String.format(RedisKeyConstant.GRAPH_ANALYSIS_DISPUTE, idCard),
                value, RedisKeyConstant.EXPIRE_TIME, TimeUnit.SECONDS);
    }

    /**
     * 从 Redis 中获取图谱分析人事纠纷事件
     * @param idCard 身份证号
     * @return 人事纠纷事件
     */
    private DisputeEntity getFromRedis(String idCard) {
        String s = redisTemplate.opsForValue().get(String.format(RedisKeyConstant.GRAPH_ANALYSIS_DISPUTE, idCard));
        return JSONObject.parseObject(s, DisputeEntity.class);
    }

    /**
     * 将图谱分析的人事纠纷放入到 Redis 中
     * @param disputeId 身份证号
     * @param value  人事纠纷事件
     */
    public void putDisputeInfoToRedis(String disputeId, String value, DisputeSourceEnum disputeSourceEnum) {
        String key = null;
        switch (disputeSourceEnum) {
            case POLICE_110:
                key = String.format(RedisKeyConstant.DISPUTE_INFO_110, disputeId);
                break;
            case COLLABORATIVE_CASE:
                key = String.format(RedisKeyConstant.DISPUTE_INFO_COLLABORATIVE_CASE, disputeId);
                break;
        }
        assert key != null;
        redisTemplate.opsForValue().set(key,
                value, RedisKeyConstant.EXPIRE_TIME, TimeUnit.SECONDS);
    }


    /**
     * 从 Redis 中获取案件详细信息
     * @param disputeId 案件编号
     * @return 案件详细信息
     */
    @Nullable
    private Object loadFromRedis(String disputeId) {

        if (Boolean.TRUE.equals(redisTemplate.hasKey(String.format(RedisKeyConstant.DISPUTE_INFO_110, disputeId)))) {
            String s = redisTemplate.opsForValue().get(String.format(RedisKeyConstant.DISPUTE_INFO_110, disputeId));
            return JSONObject.parseObject(s, Police110CaseDO.class);
        }
        if (Boolean.TRUE.equals(redisTemplate.hasKey(String.format(RedisKeyConstant.DISPUTE_INFO_COLLABORATIVE_CASE, disputeId)))) {
            String s = redisTemplate.opsForValue().get(String.format(RedisKeyConstant.DISPUTE_INFO_COLLABORATIVE_CASE, disputeId));
            return JSONObject.parseObject(s, CollaborativeCaseDO.class);
        }
        log.info("load from proxy web, disputeId is {}", disputeId);
        return this.proxyGetAndCacheDisputeInfo(disputeId);
    }

    @Nullable
    private Object proxyGetAndCacheDisputeInfo(String disputeId) {
        String url = this.proxyUrlProperties.getProxyUrl().getKg() + KGUrl.DISPUTE_DETAIL + "?searchValue=" + disputeId;
        JSONObject jsonObject = this.restTemplate.getForObject(url, JSONObject.class);
        if (jsonObject == null || jsonObject.get("code") == null || !"00000".equals(jsonObject.get("code"))) {
            throw new RuntimeException("接口调用异常");
        }
        JSONObject dateJsonObject = jsonObject.getJSONObject("data");
        KGBaseDisputeInfoDO kgBaseDisputeInfoDO = dateJsonObject.toJavaObject(KGBaseDisputeInfoDO.class);
        DisputeSourceEnum disputeSourceEnum = DisputeSourceEnum.fromDesc(kgBaseDisputeInfoDO.getDisputeSource());
        if (disputeSourceEnum == null) {
            return null;
        }
        Object disputeInfo = dateJsonObject.toJavaObject(disputeSourceEnum.getClazz());
        if (!(disputeInfo instanceof KGBaseDisputeInfoDO)) {
            return null;
        }
        KGBaseDisputeInfoDO realKgBaseDisputeInfoDO = (KGBaseDisputeInfoDO) disputeInfo;
        this.putDisputeInfoToRedis(realKgBaseDisputeInfoDO.getDisputeId(), dateJsonObject.toJSONString(), disputeSourceEnum);
        return disputeInfo;
    }

    /**
     * 从 Redis 中获取案件详细信息
     * @param disputeIds 案件编号
     * @return 案件详细信息
     */
    public List<Police110CaseDO> getPoliceCasesFromRedis(List<String> disputeIds) {
        return disputeIds.stream().filter(disputeId -> Boolean.TRUE.equals(redisTemplate.hasKey(String.format(RedisKeyConstant.DISPUTE_INFO_110, disputeId))))
                .map(disputeId -> redisTemplate.opsForValue().get(String.format(RedisKeyConstant.DISPUTE_INFO_110, disputeId)))
                .map(s -> JSONObject.parseObject(s, Police110CaseDO.class)).collect(Collectors.toList());
    }

    public List<CollaborativeCaseDO> getCollaborativeCasesFromRedis(List<String> disputeIds) {
        return disputeIds.stream().filter(disputeId -> Boolean.TRUE.equals(redisTemplate.hasKey(String.format(RedisKeyConstant.DISPUTE_INFO_COLLABORATIVE_CASE, disputeId))))
                .map(disputeId -> redisTemplate.opsForValue().get(String.format(RedisKeyConstant.DISPUTE_INFO_COLLABORATIVE_CASE, disputeId)))
                .map(s -> JSONObject.parseObject(s, CollaborativeCaseDO.class)).collect(Collectors.toList());
    }

    /**
     * 调用 nebula 数据库获取人事纠纷
     * @param IdCard 身份证号
     * @return
     */
    public DisputeEntity getGraphAtlas(String IdCard) {

        // 判断redis 中是否存在 IdCard
        Boolean existsInRedis = redisTemplate.hasKey(String.format(RedisKeyConstant.GRAPH_ANALYSIS_DISPUTE, IdCard));
        if (Objects.equals(existsInRedis, Boolean.TRUE)) {
            return getFromRedis(IdCard);
        }

        String url = this.proxyUrlProperties.getProxyUrl().getKg() + KGUrl.GRAPH_ANALYSIS + "?searchValue=" + IdCard;
        JSONObject jsonObject = this.restTemplate.getForObject(url, JSONObject.class);
        if (jsonObject == null || jsonObject.get("code") == null || !"00000".equals(jsonObject.get("code"))) {
            throw new RuntimeException("接口调用异常");
        }

        DisputeEntityBuilderDto disputeEntityBuilderDto = DisputeEntityBuilder.convertFromJsonObject(jsonObject);
        Map<Integer, Map<String, JSONObject>> disputeContentMap = disputeEntityBuilderDto.getDisputeContentMap();
        DisputeEntity disputeEntity = disputeEntityBuilderDto.getDisputeEntity();

        for (DisputeSourceEnum disputeSourceEnum : DisputeSourceEnum.values()) {
            for (Map.Entry<String, JSONObject> e : disputeContentMap.getOrDefault(disputeSourceEnum.getCode(), new HashMap<>()).entrySet()) {
                this.putDisputeInfoToRedis(e.getKey(), e.getValue().toString(), disputeSourceEnum);
            }
        }

        putDisputeEntityToRedis(IdCard, JSONObject.toJSONString(disputeEntity));

        return disputeEntity;
    }



    /**
     * 根据 身份证列表 获取所有案件的关联方式
     * @param idCard          身份证号
     * @param associationTypeEnum 关联枚举
     * @param statisticsTypeForInputEnum  纠纷统计枚举
     * @return
     */
    public List<String> obtainDisputeRelatedType(String idCard,
                                                 AssociationTypeEnum associationTypeEnum,
                                                 StatisticsTypeForInputEnum statisticsTypeForInputEnum) {
        log.info("obtainDisputeRelatedType called, id is {}, associationTypeEnum is {}, statisticsTypeForInputEnum is {}",
                idCard, associationTypeEnum, statisticsTypeForInputEnum);
        DisputeEntity disputeEntity = getGraphAtlas(idCard);
        List<String> resultDisputeIds = new ArrayList<>();
        Map<Integer, List<String>> entityAssociatedType = disputeEntity.getAssociatedTypeMap();
        if (AssociationTypeEnum.ALL == associationTypeEnum) {
            resultDisputeIds.addAll(disputeEntity.getDisputeIdList());
        } else {
            resultDisputeIds.addAll(entityAssociatedType.getOrDefault(associationTypeEnum.getCode(), new ArrayList<>()));
        }
        Map<Integer, List<String>> disputeSourceMap = disputeEntity.getDisputeSourceMap();
        switch (statisticsTypeForInputEnum) {
            case SUM:
                return resultDisputeIds;
            case POLICE:
                return ListUtils.intersection(resultDisputeIds, disputeSourceMap.getOrDefault(DisputeSourceEnum.POLICE_110.getCode(), new ArrayList<>()));
            case CASE:
                List<String> strings = disputeSourceMap.getOrDefault(DisputeSourceEnum.COLLABORATIVE_CASE.getCode(), new ArrayList<>());
                return ListUtils.intersection(resultDisputeIds, strings);
            case HIGH_RISK:
                return ListUtils.intersection(resultDisputeIds, disputeSourceMap.getOrDefault(DisputeSourceEnum.HIGH_RISK.getCode(), new ArrayList<>()));
        }
        return resultDisputeIds;
    }


    /**
     * 获取案件信息
     * @param disputeId 案件id
     * @return disputeInfo
     */
    @Nullable
    public Object obtainDisputeInfo(String disputeId) {
        return loadFromRedis(disputeId);
    }

    /**
     * 获取案件信息
     * @param disputeIds 案件id列表
     * @return case
     */
    public List<Object> obtainDisputeInfoList(List<String> disputeIds) {
        List<Object> resultList = new ArrayList<>();
        for (String disputeId : disputeIds) {
            resultList.add(obtainDisputeInfo(disputeId));
        }
        return SortDisputeInfoUtil.sortDisputeInfo(resultList);
    }

    public Map<String, Object> obtainDisputeInfoMap(List<String> disputeIds) {
        Map<String, Object> resultMap = new HashMap<>();
        for (String disputeId : disputeIds) {
            resultMap.put(disputeId, obtainDisputeInfo(disputeId));
        }
        return resultMap;
    }

    /**
     * 获取案件的纠纷类型
     * @param disputeId 纠纷案件
     * @return
     */
    public String obtainCaseDisputerType(String disputeId) {
        Object o = obtainDisputeInfo(disputeId);
        // TODO 获取案件纠纷类型
        if (o instanceof Police110CaseDO) {

        }
        return "";
    }

    /**
     * 获取案件的纠纷时间
     * @param disputeInfoList 纠纷案件详情
     * @return
     */
    public List<Date> obtainDisputeTime(List<Object> disputeInfoList) {
        List<Date> result = new ArrayList<>();
        for (Object disputeInfo : disputeInfoList) {
            if (!(disputeInfo instanceof HasDisputeTimeSortField)) {
                 continue;
            }
            Date disputeTime = ((HasDisputeTimeSortField) disputeInfo).getDisputeTimeForSort();
            if (disputeTime == null) {
                continue;
            }
            result.add(disputeTime);
        }
        return result;
    }

    public List<String> obtainDisputeType(List<Object> disputeInfoList) {
        List<String> result = new ArrayList<>();
        for (Object disputeInfo : disputeInfoList) {
            if (!(disputeInfo instanceof HasDisputeType)) {
                continue;
            }
            String disputeType = ((HasDisputeType) disputeInfo).getDisputeType();
            if (disputeType == null) {
                continue;
            }
            result.add(disputeType);
        }
        return result;
    }
}
